package com.qwc.blog.service.impl;

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.plugins.pagination.PageDTO;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qwc.blog.common.exception.BadRequestException;
import com.qwc.blog.entity.User;
import com.qwc.blog.entity.vo.user.*;
import com.qwc.blog.mapper.UserMapper;
import com.qwc.blog.service.IUserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

import static com.qwc.blog.common.constant.CommonConstant.USER_CACHE;
import static com.qwc.blog.common.constant.ResponseConstant.*;

/**
 * <p>
 * 用户信息表 服务实现类
 * </p>
 *
 * @author qwc
 * @since 2021-10-15
 */
@Slf4j
@Service
@CacheConfig(cacheNames = USER_CACHE)
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Override
    @Cacheable(key = "#username")
    public User getUser(String username) {
        return super.baseMapper.selectOne(Wrappers.<User>lambdaQuery().eq(User::getUsername, username));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @CacheEvict
    public void updateProfile(User currentUser, PutProfileDto dto) {
        currentUser.setNickname(dto.getNickname());
        currentUser.setPhoneNumber(dto.getPhoneNumber());
        currentUser.setSex(dto.getSex());
        currentUser.setEmail(dto.getEmail());
        currentUser.setUpdateBy(currentUser.getUsername());
        checkUpdateUserParam(currentUser);
        baseMapper.updateById(currentUser);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @CacheEvict
    public void updatePassword(User user, UpdatePasswordDto dto) {
        if (!passwordEncoder.matches(dto.getOldPassword(), user.getPassword())) {
            throw new BadRequestException(WRONG_PASSWORD);
        }
        if (passwordEncoder.matches(dto.getNewPassword(), user.getPassword())) {
            throw new BadRequestException(SAME_PASSWORD);
        }
        user.setPassword(passwordEncoder.encode(dto.getNewPassword()));
        user.setUpdateBy(user.getUsername());
        baseMapper.updateById(user);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @CacheEvict
    public void updateAvatar(User user, String avatar) {
        baseMapper.update(null,
                Wrappers.<User>lambdaUpdate()
                        .eq(User::getUsername, user.getUsername())
                        .set(StringUtils.isNotBlank(avatar), User::getAvatar, avatar)
                        .set(User::getUpdateBy, user.getUsername()));
    }

    @Override
    public Page getUserList(UserListReqDto dto) {
        LambdaQueryWrapper<User> queryWrapper = Wrappers.<User>lambdaQuery()
                .eq(StringUtils.isNotBlank(dto.getStatus()), User::getStatus, dto.getStatus())
                .like(StringUtils.isNotBlank(dto.getNickname()), User::getNickname, dto.getNickname())
                .eq(StringUtils.isNotBlank(dto.getPhoneNumber()), User::getPhoneNumber, dto.getPhoneNumber())
                .gt(StringUtils.isNotBlank(dto.getParams().get("beginTime")), User::getCreateTime, dto.getParams().get("beginTime"))
                .lt(StringUtils.isNotBlank(dto.getParams().get("endTime")), User::getCreateTime, dto.getParams().get("endTime"));
        Integer count = baseMapper.selectCount(queryWrapper);
        return baseMapper.selectPage(PageDTO.of(dto.getPageNum(), dto.getPageSize(), count), queryWrapper);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addUser(AddUserDto dto, User currentUser) {
        User user = dto.createUser();
        checkUpdateUserParam(user);
        user.setCreateBy(currentUser.getUsername());
        user.setUpdateBy(currentUser.getUsername());
        user.setPassword(passwordEncoder.encode(user.getPassword()));
        baseMapper.insert(user);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateUser(User user) {
        checkUpdateUserParam(user);
        baseMapper.updateById(user);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void resetPassword(User user) {
        user.setPassword(passwordEncoder.encode(user.getPassword()));
        baseMapper.updateById(user);
    }

    private void checkUpdateUserParam(User user) {
        List<User> users = baseMapper.selectList(Wrappers.<User>lambdaQuery()
                .eq(User::getUsername, user.getUsername())
                .or()
                .eq(User::getPhoneNumber, user.getPhoneNumber())
                .or()
                .eq(User::getEmail, user.getEmail())
                .or()
                .eq(User::getNickname, user.getNickname()));
        for (User u : users) {
            if (u.getUserId().equals(user.getUserId())) {
                continue;
            }
            if (u.getUsername().equals(user.getUsername())) {
                throw new BadRequestException(USED_USERNAME);
            }
            if (u.getNickname().equals(user.getNickname())) {
                throw new BadRequestException(USED_NICKNAME);
            }
            if (u.getPhoneNumber().equals(user.getPhoneNumber())) {
                throw new BadRequestException(USED_PHONE_NUMBER);
            }
            if (u.getEmail().equals(user.getEmail())) {
                throw new BadRequestException(USED_EMAIL);
            }
        }
    }
}
